package com.scarecrow.olga.game {
	
	import com.scarecrow.olga.error.VerboseError;
	import com.scarecrow.olga.game.component.GameComponent;
	import com.scarecrow.olga.util.logging.Logger;
	import flash.utils.Dictionary;
	
	/**
	 * The ComponentManager class contains references to all components created by the game,
	 * and it is useful for retrieving components of a specific type to perform game-wide 
	 * operations such as collision detection or Z sorting.
	 * 
	 * GameComponents remain registered at the Manager as long as they are attached to an owning
	 * GameObject(the GameObject class is responsible for registering and releasing them).
	 * ...
	 * @author Santiago.J.Elizalde
	 */
	public class ComponentManager {
		
		// ----------- Static -------------
		
		private static var componentsByType:Dictionary;
		
		/**
		 * Registers a component in the ComponentManager to be retrieved later.
		 * Components are stored and retrieved by their type.
		 * 
		 * @param	component the component that needs to be registered.
		 * @throws	registry error if the component is already registered.
		 */
		public static function registerComponent(component:GameComponent):void {
			
			if (!componentsByType) {
				componentsByType = new Dictionary();
			}
			
			if (!componentsByType[component.type]) {
				componentsByType[component.type] = new Vector.<GameComponent>();
			} else {
				if (componentsByType[component.type].indexOf(component) >= 0) {
					throw new VerboseError("ComponentManager registry error.", "the supplied GameComponent is already registered.", { component:component.toString(), owner:component.owner ? component.owner.name : "null"} );
				}
			}
			
			componentsByType[component.type].push(component);
		}
		
		/**
		 * Releases a registered component. Released components can no longer be reached
		 * through the getComponents() method in the manager.
		 * 
		 * Components are released by their owning GameObjects on removing them from 
		 * their component list.
		 * 
		 * @param	component	The component that needs to be released.
		 * @see		GameObject's removeComponent() method.
		 */
		public static function releaseComponent(component:GameComponent):void {
			
			if (!componentsByType) {
				Logger.log("Unable to release component, there are no components registered.", Logger.LOG_LEVEL_WARN);
				return;
			}
			
			if (!componentsByType[component.type]) {
				Logger.log("Unable to release component, there is no component of type '" + component.type + "' registered.", Logger.LOG_LEVEL_WARN);
				return;
			}
			
			var componentList:Vector.<GameComponent> = componentsByType[component.type];
			var idx:int = componentList.indexOf(component);
			
			if (idx >= 0) {
				componentList.splice(idx, 1);
			} else {
				Logger.log("Unable to release component, the specified component is not registered.", Logger.LOG_LEVEL_WARN);
			}
		}
		
		/**
		 * Returns a vector containing all registered components of the specified type.
		 * 
		 * @param	type	The type of the components to return.
		 * @return	a vector of GameComponent objects of the type specified.
		 */
		public static function getComponents(type:Class):Vector.<GameComponent> {
			
			if (!type in componentsByType) {
				Logger.log("There are no components of type '" + String(type) + "' registered in the componentManager.", Logger.LOG_LEVEL_WARN);
				return new Vector.<GameComponent>();
			}
			
			//CHECK: There may be a bug in the AS3 Vector class which returns the same instance when calling an empty concat() method.
			return componentsByType[type].concat();
		}
		
		// --------- Instance -----------------
		
		public function ComponentManager() {
			
		}
	}
}